package xpn.platform.common.exception;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;
import xpn.platform.common.response.ResponseDTO;
import xpn.platform.common.response.ResponseUtil;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 异常统一处理 监听所有Controller，抛出异常后执行
 *
 * @author duwei
 */

@ControllerAdvice
@ResponseBody
@Slf4j
public class XpnExceptionProcessorAdvice {
	/*
	 * 业务异常
	 */
	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler(XpnBusinessException.class)
	ResponseDTO handleBusinessException(XpnBusinessException e, HttpServletResponse response) {
		log.error(e.getMessage(), e);
		return ResponseUtil.fail(e);
	}

	/*
	 * 认证异常：未登录而去访问需要身份认证的资源
	 */
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	@ExceptionHandler({ XpnAuthenticationException.class })
	public ResponseDTO handleXpnAuthenticationException(XpnAuthenticationException e, HttpServletResponse response) {
		log.error(e.getMessage(), e);
		return ResponseUtil.fail(XpnExceptionCodeEnum.NEED_AUTHENTICATION, e.getMessage());
	}

	/*
	 * Shiro验证异常
	 */
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	@ExceptionHandler({ AuthorizationException.class })
	public ResponseDTO handleShiroAuthorizationException(ShiroException e) {
		log.error(e.getMessage(), e);
		return ResponseUtil.fail(XpnExceptionCodeEnum.SHIRO_ERROR, e.getMessage());
	}

	/*
	 * Shiro异常
	 */
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	@ExceptionHandler({ ShiroException.class })
	public ResponseDTO handleShiroException(ShiroException e) {
		log.error(e.getMessage(), e);
		return ResponseUtil.fail(XpnExceptionCodeEnum.SHIRO_ERROR, e.getMessage());
	}

	/**
	 * 授权异常：访问无权限访问的资源
	 */
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	@ExceptionHandler({ XpnAuthorizationException.class })
	public ResponseDTO handleXpnAuthorizationException(ShiroException e) {
		log.error(e.getMessage(), e);
		return ResponseUtil.fail(XpnExceptionCodeEnum.NEED_PERMISSION, e.getMessage());
	}

	/*
	 * Spring Data Rest 校验异常
	 */
	// 校验失败信息
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(RepositoryConstraintViolationException.class)
	public ResponseDTO handleConstraintViolationException(RepositoryConstraintViolationException e) {
		List<Map<String, String>> errorMsg = new ArrayList<>();
		for (FieldError error : e.getErrors().getFieldErrors()) {
			Map<String, String> errorMsgItem = new HashMap<>();
			errorMsgItem.put(error.getField(), error.getDefaultMessage());
			errorMsg.add(errorMsgItem);
		}
		log.error(errorMsg.toString(), e);
		return ResponseUtil.fail(XpnExceptionCodeEnum.VALIDATION_FAILED, errorMsg);
	}

	/*
	 * controller校验异常
	 */
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(ConstraintViolationException.class)
	public ResponseDTO handleConstraintViolationException(ConstraintViolationException e) {
		List<Map<String, String>> errorMsg = new ArrayList<>();
		for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
			Map<String, String> errorMsgItem = new HashMap<>();
			errorMsgItem.put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage());
			errorMsg.add(errorMsgItem);
		}
		log.error(errorMsg.toString(), e);
		return ResponseUtil.fail(XpnExceptionCodeEnum.VALIDATION_FAILED, errorMsg);
	}

	/*
	 * 路径无对应的handler异常
	 */
	@ResponseStatus(HttpStatus.NOT_FOUND)
	@ExceptionHandler(NoHandlerFoundException.class)
	ResponseDTO handleNoHandlerFoundException(NoHandlerFoundException e) {
		String errorMsg = e.getMessage();
		log.error(errorMsg, e);
		return ResponseUtil.fail(HttpServletResponse.SC_NOT_FOUND, errorMsg);
	}

	/*
	 * Spring Data Rest未找到资源异常
	 */
	@ResponseStatus(HttpStatus.NOT_FOUND)
	@ExceptionHandler(ResourceNotFoundException.class)
	ResponseDTO handleResourceNotFoundException(ResourceNotFoundException e) {
		String errorMsg = e.getMessage();
		log.error(errorMsg, e);
		return ResponseUtil.fail(HttpServletResponse.SC_NOT_FOUND, errorMsg);
	}

	/*
	 * 系统异常
	 */
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(Throwable.class)
	ResponseDTO handleThrowableException(Throwable e) {
		String errorMsg = e.getMessage();
		log.error(errorMsg, e);
		return ResponseUtil.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), errorMsg);
	}
}
